import { select } from 'd3-selection'
import rough from 'roughjs'
import topicShape from '../shape'
import summaryLineShape from '../summary-line'
import { getXmindSummaryPos, getVerticalXmindSummaryPos } from '../utils/node'
import mitter from '@/mitt'

let container = null
export let rc = null
const highLightSummarySpacing = 3 // 概要信息高亮边框边距
const highLightTopicSpacing = 8 // 概要包围主题的高亮边框边距
export const summaryRoughOptions = {
  fillStyle: 'hachure',
  seed: 52,
  fillWeight: 6,
  hachureGap: 6.4,
  bowing: 1,
  roughness: 1.2
}
export const summaryNoRoughOptions = {
  fillStyle: 'solid',
  bowing: 0,
  roughness: 0
}

export function graphSummaryContainer (mindContainer) {
  rc = rough.svg(mindContainer)
  container = mindContainer
    .append('g')
    .attr('class', 'mind-map-summarybox')
  return container
}

/**
 * 渲染绘制节点概要视图
 * @param {*} id
 */
export function graphSummaryElements (id, nodeContainer) {
  container.selectAll('g').remove()
  nodeContainer
    .selectAll('.x-mind-nodetheme')
    .filter(node => node.data.targetSummarys?.length)
    .each(node => {
      drawTopicSummaryNodes(node)
    })
  id && requestAnimationSummary(id)
}

/**
 * 主题概要的信息绘制
 * @param {*} node
 */
function drawTopicSummaryNodes (node) {
  const summarys = node.data.targetSummarys
  const horizontal = ['left', 'right'].includes(node.direction)
  const minValue = horizontal ? getXmindSummaryPos(node).minY : getVerticalXmindSummaryPos(node).minX
  for (let i = 0; i < summarys.length; i++) {
    const { id: tId, style, summaryWidth, summaryHeight, text, paddingLR, paddingTB } = summarys[i]
    const targetTopicNode = select(`#${tId}`)
    if (targetTopicNode.empty()) continue
    if (targetTopicNode.datum().parent?.data._id !== node.parent?.data._id) continue
    const brotherTopics = targetTopicNode.datum().parent?.children
    if (!brotherTopics?.length) continue
    const { startX, startY, maxX, maxY, minX, minY, centerPosition, rectX, rectY, x, y, width, height, upArea, downArea, unit, forwardDirection } = getSummaryCalculateOptions(node.data._id, brotherTopics, summarys[i], node.direction, minValue)
    container.append('g')
      .attr('id', `summary-path-${node.data._id}-${tId}`)
      .attr('data-id', `${node.data._id}-${tId}`)
      .append('g')
      .attr('class', 'summary-line-path')
      .html(() => {
        const path = horizontal ? summaryLineShape[style.lineShape].horizontal({ x: startX, y: minY }, { x: startX, y: maxY }, node.direction)
          : summaryLineShape[style.lineShape].vertical({ x: minX, y: startY }, { x: maxX, y: startY }, node.direction)
        const n = rc.path(path, {
          fill: 'none',
          stroke: style.lineColor,
          strokeWidth: 2,
          ...(style.isRough ? summaryRoughOptions : summaryNoRoughOptions)
        })
        return select(n).html()
      })
    container.select(`#summary-path-${node.data._id}-${tId}`).append('g')
      .attr('class', 'summary-topic-path')
      .html(() => {
        const generateShapePath = topicShape[style.topicShape].generateShapePath
        const path = generateShapePath(rectX, rectY, summaryWidth, summaryHeight)
        const n = rc.path(path, {
          fill: style.topicFill,
          stroke: style.topicStrokeColor,
          strokeWidth: 2,
          ...(style.isRough ? summaryRoughOptions : summaryNoRoughOptions)
        })
        return select(n).html()
      })
    container.select(`#summary-path-${node.data._id}-${tId}`).append('g').attr('class', 'summary-data-topic')
      .datum(
        {
          direction: node.direction,
          forwardDirection,
          parentId: `${node.data._id}-${tId}`,
          upArea,
          downArea,
          x,
          y,
          width,
          height,
          style,
          startX,
          startY,
          minX,
          minY,
          maxX,
          maxY,
          rectX,
          rectY,
          summaryWidth,
          summaryHeight,
          unit,
          text,
          centerPosition,
          highLightSummarySpacing,
          highLightTopicSpacing,
          paddingLR,
          paddingTB
        })
      .on('mousedown', event => event.stopPropagation)
      .on('mouseenter', function (event) {
        mitter.emit('summary-handler-mouseenter', { event, _this: this })
      })
      .on('mouseleave', function (event) {
        mitter.emit('summary-handler-mouseleave', { event, _this: this })
      })
      .on('click', function (event) {
        mitter.emit('summary-handler-click', { event, _this: this })
      })
      .on('dblclick', function (event) {
        mitter.emit('summary-handler-dblclick', { event, _this: this })
      })
      .on('contextmenu', function () {
        mitter.emit('summary-handler-click', { _this: this })
      })
      .attr('fill', 'transparent')
      .append('foreignObject')
      .attr('x', () => {
        return horizontal
          ? centerPosition.x + (highLightTopicSpacing * 2 + paddingLR) * unit - (forwardDirection ? 0 : summaryWidth - paddingLR * 2)
          : centerPosition.x - summaryWidth / 2 + paddingLR
      })
      .attr('y', () => {
        return horizontal
          ? centerPosition.y - summaryHeight / 2 + paddingTB
          : centerPosition.y + (highLightTopicSpacing * 2 + paddingTB) * unit - (forwardDirection ? 0 : summaryHeight - paddingTB * 2)
      })
      .attr('width', summaryWidth - paddingLR * 2)
      .attr('height', summaryHeight - paddingTB * 2)
      .append('xhtml:p')
      .attr('xmlns', 'http://www.w3.org/1999/xhtml')
      .attr('class', 'node-summary-description')
      .style('color', style.textColor)
      .style('font-family', style.fontFamily)
      .style('font-size', style.fontSize + 'px')
      .style('font-weight', 'bold')
      .style('line-break', 'anywhere')
      .style('word-break', 'break-all')
      .text(text)
      .each(function () {
        select(this.parentNode.parentNode)
          .append('rect')
          .attr('class', 'summary-highlight-border')
          .attr('x', () => {
            return horizontal
              ? centerPosition.x + (highLightTopicSpacing * 2 - highLightSummarySpacing) * unit - (forwardDirection ? 0 : summaryWidth + highLightSummarySpacing * 2)
              : centerPosition.x - summaryWidth / 2 - highLightSummarySpacing
          })
          .attr('y', () => {
            return horizontal
              ? centerPosition.y - summaryHeight / 2 - highLightSummarySpacing
              : centerPosition.y + (highLightTopicSpacing * 2 - highLightSummarySpacing) * unit - (forwardDirection ? 0 : summaryHeight + highLightSummarySpacing * 2)
          })
          .attr('width', summaryWidth + highLightSummarySpacing * 2)
          .attr('height', summaryHeight + highLightSummarySpacing * 2)
          .attr('rx', 4)
          .attr('ry', 4)
          .attr('stroke', 'none')
          .attr('stroke-width', 2)
          .attr('fill', 'transparent')
      })
  }
}

/**
 * 水平方向和竖直方向上概要数学数据信息获取
 * @param {*} sId
 * @param {*} brotherTopics
 * @param {*} summary
 * @param {*} direction
 * @param {*} minValue
 * @returns
 */
function getSummaryCalculateOptions (sId, brotherTopics, summary, direction, minValue) {
  const { id: tId, summaryWidth, summaryHeight } = summary
  const horizontal = ['left', 'right'].includes(direction)
  const startIdx = brotherTopics.findIndex(o => o.data._id === sId)
  const endIdx = brotherTopics.findIndex(o => o.data._id === tId)
  const forwardDirection = ['right', 'bottom'].includes(direction)
  const unit = forwardDirection ? 1 : -1
  const targetTopicNode = select(`#${tId}`)
  if (horizontal) {
    let [minX, maxX] = [Infinity, -Infinity]
    const [upArea, downArea] = [[], []]
    downArea[1] = getXmindSummaryPos(brotherTopics[brotherTopics.length - 1]).maxY + highLightTopicSpacing
    upArea[0] = getXmindSummaryPos(brotherTopics[0]).minY - highLightTopicSpacing
    for (let i = startIdx; i <= endIdx; i++) {
      const limitRect = getXmindSummaryPos(brotherTopics[i])
      i === startIdx && (downArea[0] = limitRect.maxY + highLightTopicSpacing)
      i === endIdx && (upArea[1] = limitRect.minY - highLightTopicSpacing)
      maxX = Math.max(maxX, limitRect.maxX)
      minX = Math.min(minX, limitRect.minX)
    }
    const maxY = getXmindSummaryPos(targetTopicNode.datum()).maxY
    const startX = forwardDirection ? maxX : minX
    const centerPosition = { x: startX, y: (maxY + minValue) / 2 }
    const rectX = centerPosition.x + highLightTopicSpacing * 2 * unit - (forwardDirection ? 0 : summaryWidth)
    const rectY = centerPosition.y - summaryHeight / 2
    const x = minX + (forwardDirection ? -highLightTopicSpacing : highLightTopicSpacing)
    const y = minValue - highLightTopicSpacing
    const width = maxX - minX
    const height = maxY - minValue + highLightTopicSpacing * 2
    return {
      startX, startY: 0, maxX, maxY, minX, minY: minValue, centerPosition, rectX, rectY, x, y, width, height, upArea, downArea, unit, forwardDirection
    }
  } else {
    let [minY, maxY] = [Infinity, -Infinity]
    const [upArea, downArea] = [[], []]
    downArea[1] = getVerticalXmindSummaryPos(brotherTopics[brotherTopics.length - 1]).maxX + 8
    upArea[0] = getVerticalXmindSummaryPos(brotherTopics[0]).minX - 8
    for (let i = startIdx; i <= endIdx; i++) {
      const limitRect = getVerticalXmindSummaryPos(brotherTopics[i])
      i === startIdx && (downArea[0] = limitRect.maxX + 8)
      i === endIdx && (upArea[1] = limitRect.minX - 8)
      maxY = Math.max(maxY, limitRect.maxY)
      minY = Math.min(maxY, limitRect.minY)
    }
    const maxX = getVerticalXmindSummaryPos(targetTopicNode.datum()).maxX
    const startY = forwardDirection ? maxY : minY
    const centerPosition = { x: (maxX + minValue) / 2, y: startY }
    const rectX = centerPosition.x - summaryWidth / 2
    const rectY = centerPosition.y + 16 * unit - (forwardDirection ? 0 : summaryHeight)
    const x = minValue - highLightTopicSpacing
    const y = minY + (forwardDirection ? -highLightTopicSpacing : highLightTopicSpacing)
    const width = maxX - minValue + highLightTopicSpacing * 2
    const height = maxY - minY

    return {
      startX: 0, startY, maxX, maxY, minX: minValue, minY, centerPosition, rectX, rectY, x, y, width, height, upArea, downArea, unit, forwardDirection
    }
  }
}

/**
 * 动画渲染指定id的概要节点
 * @param {*} id
 */
function requestAnimationSummary (id) {
  select(`#summary-path-${id}`)
    .selectAll('path, .rect-border')
    .attr('stroke-dasharray', function () {
      const len = select(this).node().getTotalLength()
      return `0 ${len}`
    })
    .transition()
    .duration(600)
    .attr('stroke-dasharray', function () {
      const len = select(this).node().getTotalLength()
      return `${len}, 0`
    })
  select(`#summary-path-${id}`)
    .select('text')
    .attr('opacity', 0)
    .transition()
    .delay(500)
    .duration(500)
    .attr('opacity', 1)
}
